#include <QPainter>
#include <QTimer>
#include <QMouseEvent>
#include <QMessageBox>//显示信息对话框
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QDebug>
#include <math.h>
#include "mainwindow.h"

// -------全局遍历-------//

const int Margin = 30; // 棋盘边缘空隙
const int CELL_SIZE = 40; // 格子的大小

const int AI_THINK_TIME = 700; // AI下棋的思考时间

// -------------------- //

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // 设置棋盘大小，固定尺寸
    setFixedSize(Margin * 2 + CELL_SIZE * (BOARD_SIZE-1), Margin * 2 + CELL_SIZE * (BOARD_SIZE-1));

    //设置标题
    setWindowTitle("五子棋游戏");

    // 开启鼠标hover功能，便于监听棋子
    setMouseTracking(true);

    // 添加菜单
    QMenu *gameMenu = menuBar()->addMenu(tr("Game")); // menuBar默认是存在的，直接加菜单就可以了

    //创建一个新的QAction对象，即菜单项，其显示文本为“Person VS Person”
    QAction *actionPVP = new QAction("Person VS Person", this);
    //connect：这是一个信号槽连接函数，用于将信号与槽函数连接起来，以实现事件处理
    //actionPVP：发送信号的对象，即刚才创建的菜单项。
    //SIGNAL(triggered())：当菜单项被触发（例如被点击）时，会发出triggered()信号。
    //this：接收信号的对象，通常是指当前窗口或对话框。
    //SLOT(initPVPGame())：当triggered()信号发出时，会调用initPVPGame()槽函数。这个槽函数应该是在当前窗口或对话框类中定义的，用于初始化“Person VS Person”游戏模式。
    connect(actionPVP, SIGNAL(triggered()), this, SLOT(initPVPGame()));
    gameMenu->addAction(actionPVP);//将刚才创建的菜单项actionPVP添加到gameMenu菜单中。

    QAction *actionPVE = new QAction("Person VS Computer", this);
    connect(actionPVE, SIGNAL(triggered()), this, SLOT(initPVEGame()));
    gameMenu->addAction(actionPVE);

    // 开始游戏
    initGame();
}

MainWindow::~MainWindow()//析构函数
{
    if (game)//game非空
    {
        delete game;//释放game指针所指对象所占的内存
        game = nullptr;
    }
}

void MainWindow::initGame()
{
    // 初始化游戏模型
    game = new GameModel;
    initPVEGame();//初始化定义人机模式
}

void MainWindow::initPVPGame()
{
    game_type = PERSON;
    game->gameStatus = PLAYING;
    game->startGame(game_type);
    update();
}

void MainWindow::initPVEGame()
{
    game_type = BOT;
    game->gameStatus = PLAYING;
    game->startGame(game_type);
    update();
}

void MainWindow::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    // 绘制棋盘
    painter.setRenderHint(QPainter::Antialiasing, true); // 抗锯齿,使边缘更平滑，效果美观
    for (int i = 0; i < BOARD_SIZE; i++)
    {
        //（x1,y1,x2,y2)从（x1,y1）画到（x2,y2)
        painter.drawLine(Margin+i*CELL_SIZE,Margin,
                         Margin+i*CELL_SIZE,Margin+CELL_SIZE*(BOARD_SIZE-1));
        painter.drawLine(Margin,Margin+i*CELL_SIZE,
                         Margin+(BOARD_SIZE-1)*CELL_SIZE,Margin+i*CELL_SIZE);
    }

    QBrush brush;
    brush.setStyle(Qt::SolidPattern);//实心

    // 绘制落子标记(防止鼠标出框越界)
    if (clickRow >= 0 && clickRow < BOARD_SIZE&&
        clickCol >= 0 && clickCol < BOARD_SIZE &&
        game->gameMapVec[clickRow][clickCol] == 0)//该位置空白才能下棋
    {
        if (game->playerFlag)
            brush.setColor(Qt::white);
        else
            brush.setColor(Qt::black);
        painter.setBrush(brush);
        painter.drawEllipse(QPoint(Margin+clickCol*CELL_SIZE,Margin+clickRow*CELL_SIZE),//坐标位置
                            CELL_SIZE/6,CELL_SIZE/6);//悬停点图形大小
    }

    // 绘制棋子，1为白，-1为黑
    for (int i = 0; i < BOARD_SIZE; i++)
        for (int j = 0; j < BOARD_SIZE; j++)
        {
            if (game->gameMapVec[i][j] == 1)
            {
                brush.setColor(Qt::white);
                painter.setBrush(brush);
                painter.drawEllipse(QPoint(Margin+j*CELL_SIZE,Margin+i*CELL_SIZE),
                                    CELL_SIZE/3,CELL_SIZE/3);

            }
            else if (game->gameMapVec[i][j] == -1)
            {
                brush.setColor(Qt::black);
                painter.setBrush(brush);             
                painter.drawEllipse(QPoint(Margin+j*CELL_SIZE,Margin+i*CELL_SIZE),
                                    CELL_SIZE/3,CELL_SIZE/3);
            }
        }

    // 判断输赢
    if (clickRow > 0 && clickRow < BOARD_SIZE &&
        clickCol > 0 && clickCol < BOARD_SIZE &&
        (game->gameMapVec[clickRow][clickCol] == 1 ||
         game->gameMapVec[clickRow][clickCol] == -1))//此处要有落子，才能开始判断是否获胜
    {
        if (game->isWin(clickRow, clickCol) && game->gameStatus == PLAYING)
        {
            qDebug() << "win";// qdebug输出调试信息“win”
            game->gameStatus = WIN;//游戏状态切换成win

            QString str;
            if (game->gameMapVec[clickRow][clickCol] == 1)
                str = "white player";
            else if (game->gameMapVec[clickRow][clickCol] == -1)
                str = "black player";
            QMessageBox::StandardButton btnValue = QMessageBox::information(this, "congratulations", str + " win!");

            // 重置游戏状态，否则容易死循环
            if (btnValue == QMessageBox::Ok)
            {
                game->startGame(game_type);
                game->gameStatus = PLAYING;
            }
        }
    }


    // 判断死局
    if (game->isDeadGame())
    {
        QMessageBox::StandardButton btnValue = QMessageBox::information(this, "oops", "dead game!");
        if (btnValue == QMessageBox::Ok)
        {
            game->startGame(game_type);
            game->gameStatus = PLAYING;
        }

    }
}

void MainWindow::mouseMoveEvent(QMouseEvent*event){//找到悬停点的坐标
    int row=(event->y()-Margin+CELL_SIZE/2)/CELL_SIZE;//CELL_SIZE是误差范围
    int col=(event->x()-Margin+CELL_SIZE/2)/CELL_SIZE;
            clickRow=row;
            clickCol=col;
            update();
        }
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
    // 人下棋，并且不能抢机器的棋
    if (!(game_type == BOT && !game->playerFlag))
    {
        chessOneByPerson();
        // 如果是人机模式，需要调用AI下棋
        if (game->gameType == BOT && !game->playerFlag)
        {
            // 用定时器做一个延迟
            QTimer::singleShot(AI_THINK_TIME, this, SLOT(chessOneByAI()));
        }
    }

}

void MainWindow::chessOneByPerson()
{
    // 根据当前存储的坐标下子
    // 只有有效点击才下子，并且该处没有子
    if (clickRow != -1 && clickCol != -1 && game->gameMapVec[clickRow][clickCol] == 0)
    {
        game->actionByPerson(clickRow, clickCol);

        // 重绘
        update();
    }
}

void MainWindow::chessOneByAI()
{
    game->actionByAI(clickRow, clickCol);

    //重绘
    update();
}
